/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

var zrUtil = require("static/plugins/js/zrender/lib/core/util");

var graphic = require("../../util/graphic");

var textContain = require("static/plugins/js/zrender/lib/contain/text");

var formatUtil = require("../../util/format");

var matrix = require("static/plugins/js/zrender/lib/core/matrix");

var axisHelper = require("../../coord/axisHelper");

var AxisBuilder = require("../axis/AxisBuilder");

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

/**
 * @param {module:echarts/model/Model} axisPointerModel
 */
function buildElStyle(axisPointerModel) {
    var axisPointerType = axisPointerModel.get("type");
    var styleModel = axisPointerModel.getModel(axisPointerType + "Style");
    var style;

    if (axisPointerType === "line") {
        style = styleModel.getLineStyle();
        style.fill = null;
    } else if (axisPointerType === "shadow") {
        style = styleModel.getAreaStyle();
        style.stroke = null;
    }

    return style;
}
/**
 * @param {Function} labelPos {align, verticalAlign, position}
 */

function buildLabelElOption(
    elOption,
    axisModel,
    axisPointerModel,
    api,
    labelPos
) {
    var value = axisPointerModel.get("value");
    var text = getValueLabel(
        value,
        axisModel.axis,
        axisModel.ecModel,
        axisPointerModel.get("seriesDataIndices"),
        {
            precision: axisPointerModel.get("label.precision"),
            formatter: axisPointerModel.get("label.formatter"),
        }
    );
    var labelModel = axisPointerModel.getModel("label");
    var paddings = formatUtil.normalizeCssArray(labelModel.get("padding") || 0);
    var font = labelModel.getFont();
    var textRect = textContain.getBoundingRect(text, font);
    var position = labelPos.position;
    var width = textRect.width + paddings[1] + paddings[3];
    var height = textRect.height + paddings[0] + paddings[2]; // Adjust by align.

    var align = labelPos.align;
    align === "right" && (position[0] -= width);
    align === "center" && (position[0] -= width / 2);
    var verticalAlign = labelPos.verticalAlign;
    verticalAlign === "bottom" && (position[1] -= height);
    verticalAlign === "middle" && (position[1] -= height / 2); // Not overflow ec container

    confineInContainer(position, width, height, api);
    var bgColor = labelModel.get("backgroundColor");

    if (!bgColor || bgColor === "auto") {
        bgColor = axisModel.get("axisLine.lineStyle.color");
    }

    elOption.label = {
        shape: {
            x: 0,
            y: 0,
            width: width,
            height: height,
            r: labelModel.get("borderRadius"),
        },
        position: position.slice(),
        // TODO: rich
        style: {
            text: text,
            textFont: font,
            textFill: labelModel.getTextColor(),
            textPosition: "inside",
            textPadding: paddings,
            fill: bgColor,
            stroke: labelModel.get("borderColor") || "transparent",
            lineWidth: labelModel.get("borderWidth") || 0,
            shadowBlur: labelModel.get("shadowBlur"),
            shadowColor: labelModel.get("shadowColor"),
            shadowOffsetX: labelModel.get("shadowOffsetX"),
            shadowOffsetY: labelModel.get("shadowOffsetY"),
        },
        // Lable should be over axisPointer.
        z2: 10,
    };
} // Do not overflow ec container

function confineInContainer(position, width, height, api) {
    var viewWidth = api.getWidth();
    var viewHeight = api.getHeight();
    position[0] = Math.min(position[0] + width, viewWidth) - width;
    position[1] = Math.min(position[1] + height, viewHeight) - height;
    position[0] = Math.max(position[0], 0);
    position[1] = Math.max(position[1], 0);
}
/**
 * @param {number} value
 * @param {module:echarts/coord/Axis} axis
 * @param {module:echarts/model/Global} ecModel
 * @param {Object} opt
 * @param {Array.<Object>} seriesDataIndices
 * @param {number|string} opt.precision 'auto' or a number
 * @param {string|Function} opt.formatter label formatter
 */

function getValueLabel(value, axis, ecModel, seriesDataIndices, opt) {
    value = axis.scale.parse(value);
    var text = axis.scale.getLabel(
        // If `precision` is set, width can be fixed (like '12.00500'), which
        // helps to debounce when when moving label.
        value,
        {
            precision: opt.precision,
        }
    );
    var formatter = opt.formatter;

    if (formatter) {
        var params = {
            value: axisHelper.getAxisRawValue(axis, value),
            axisDimension: axis.dim,
            axisIndex: axis.index,
            seriesData: [],
        };
        zrUtil.each(seriesDataIndices, function (idxItem) {
            var series = ecModel.getSeriesByIndex(idxItem.seriesIndex);
            var dataIndex = idxItem.dataIndexInside;
            var dataParams = series && series.getDataParams(dataIndex);
            dataParams && params.seriesData.push(dataParams);
        });

        if (zrUtil.isString(formatter)) {
            text = formatter.replace("{value}", text);
        } else if (zrUtil.isFunction(formatter)) {
            text = formatter(params);
        }
    }

    return text;
}
/**
 * @param {module:echarts/coord/Axis} axis
 * @param {number} value
 * @param {Object} layoutInfo {
 *  rotation, position, labelOffset, labelDirection, labelMargin
 * }
 */

function getTransformedPosition(axis, value, layoutInfo) {
    var transform = matrix.create();
    matrix.rotate(transform, transform, layoutInfo.rotation);
    matrix.translate(transform, transform, layoutInfo.position);
    return graphic.applyTransform(
        [
            axis.dataToCoord(value),
            (layoutInfo.labelOffset || 0) +
                (layoutInfo.labelDirection || 1) *
                    (layoutInfo.labelMargin || 0),
        ],
        transform
    );
}

function buildCartesianSingleLabelElOption(
    value,
    elOption,
    layoutInfo,
    axisModel,
    axisPointerModel,
    api
) {
    var textLayout = AxisBuilder.innerTextLayout(
        layoutInfo.rotation,
        0,
        layoutInfo.labelDirection
    );
    layoutInfo.labelMargin = axisPointerModel.get("label.margin");
    buildLabelElOption(elOption, axisModel, axisPointerModel, api, {
        position: getTransformedPosition(axisModel.axis, value, layoutInfo),
        align: textLayout.textAlign,
        verticalAlign: textLayout.textVerticalAlign,
    });
}
/**
 * @param {Array.<number>} p1
 * @param {Array.<number>} p2
 * @param {number} [xDimIndex=0] or 1
 */

function makeLineShape(p1, p2, xDimIndex) {
    xDimIndex = xDimIndex || 0;
    return {
        x1: p1[xDimIndex],
        y1: p1[1 - xDimIndex],
        x2: p2[xDimIndex],
        y2: p2[1 - xDimIndex],
    };
}
/**
 * @param {Array.<number>} xy
 * @param {Array.<number>} wh
 * @param {number} [xDimIndex=0] or 1
 */

function makeRectShape(xy, wh, xDimIndex) {
    xDimIndex = xDimIndex || 0;
    return {
        x: xy[xDimIndex],
        y: xy[1 - xDimIndex],
        width: wh[xDimIndex],
        height: wh[1 - xDimIndex],
    };
}

function makeSectorShape(cx, cy, r0, r, startAngle, endAngle) {
    return {
        cx: cx,
        cy: cy,
        r0: r0,
        r: r,
        startAngle: startAngle,
        endAngle: endAngle,
        clockwise: true,
    };
}

exports.buildElStyle = buildElStyle;
exports.buildLabelElOption = buildLabelElOption;
exports.getValueLabel = getValueLabel;
exports.getTransformedPosition = getTransformedPosition;
exports.buildCartesianSingleLabelElOption = buildCartesianSingleLabelElOption;
exports.makeLineShape = makeLineShape;
exports.makeRectShape = makeRectShape;
exports.makeSectorShape = makeSectorShape;
